/**
 * 
 */
package uk.ac.manchester.cs.graph.io;

import java.io.IOException;
import java.io.Reader;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import uk.ac.manchester.cs.graph.DirectedGraphImpl;
import uk.ac.manchester.cs.graph.Graph;
import uk.ac.manchester.cs.graph.GraphImpl;

/**
 * A very simple SAX-based parser for GraphML
 *
 * (c) Pavel Klinov
 *
 */
public class GraphMLReaderImpl<V,E>  extends DefaultHandler implements GraphReader<V, E> {

	private GraphNodeReader<V> m_nodeReader = null;	
	private Graph<V,E> m_graph = null;
	
	@Override
	public Graph<V, E> read(Reader reader) throws IOException {
		//we do SAX parsing here
		SAXParser parser =  getSAXParser();
		InputSource input = new InputSource(reader);
		
		try {
			parser.parse(input, this);
		} catch (SAXException e) {
			throw new IOException(e);
		}
		
		return m_graph;
	}

	private SAXParser getSAXParser() throws IOException {
		SAXParserFactory factory = SAXParserFactory.newInstance();

		factory.setValidating(false);
		
	    try {
			return factory.newSAXParser();
		} catch (Throwable e) {
			throw new IOException(e);
		}
	}

	@Override
	public void setNodeReader(GraphNodeReader<V> nodeReader) {
		m_nodeReader = nodeReader;
	}

	@Override
	public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
		if (GraphMLConstants.GRAPH_ELEMENT.equals(qName)) {
			//Creating the graph
			String directed = attrs.getValue(GraphMLConstants.DIRECTED_ATTRIBUTE);
			
			m_graph = directed == null || GraphMLConstants.EDGE_UNDIRECTED_VALUE.equals(directed)
						? new GraphImpl<V,E>()
						: new DirectedGraphImpl<V,E>();
		}
		else if (GraphMLConstants.NODE_ELEMENT.equals(qName)) {
			String nodeText = attrs.getValue(GraphMLConstants.NODE_ID_ATTRIBUTE);
			
			m_graph.addVertex(m_nodeReader.read(nodeText));
		}
		else if (GraphMLConstants.EDGE_ELEMENT.equals(qName)) {
			String sourceText = attrs.getValue(GraphMLConstants.NODE_SOURCE_ATTRIBUTE);
			String targetText = attrs.getValue(GraphMLConstants.NODE_TARGET_ATTRIBUTE);
			V source = m_nodeReader.read(sourceText);
			V target = m_nodeReader.read(targetText);
			
			m_graph.addEdge(null, source, target);
		}
	}
}